//=============================================================================
//                                                                            
//                               OpenMesh                                     
//      Copyright (C) 2001-2005 by Computer Graphics Group, RWTH Aachen       
//                           www.openmesh.org                                 
//                                                                            
//-----------------------------------------------------------------------------
//                                                                            
//                                License                                     
//                                                                            
//   This library is free software; you can redistribute it and/or modify it 
//   under the terms of the GNU Library General Public License as published  
//   by the Free Software Foundation, version 2.                             
//                                                                             
//   This library is distributed in the hope that it will be useful, but       
//   WITHOUT ANY WARRANTY; without even the implied warranty of                
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         
//   Library General Public License for more details.                          
//                                                                            
//   You should have received a copy of the GNU Library General Public         
//   License along with this library; if not, write to the Free Software       
//   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                 
//                                                                            
//-----------------------------------------------------------------------------
//                                                                            
//   $Revision: 1.4 $
//   $Date: 2005-12-21 13:55:21 $
//                                                                            
//=============================================================================

/** \file ModNormalFlippingT.hh

*/

//=============================================================================
//
//  CLASS ModNormalFlipping
//
//=============================================================================


#ifndef OPENMESH_DECIMATER_MODNORMALFLIPPING_HH
#define OPENMESH_DECIMATER_MODNORMALFLIPPING_HH


//== INCLUDES =================================================================

#include <Tools/Decimater/ModBaseT.hh>

//== NAMESPACES ===============================================================

namespace OpenMesh { // BEGIN_NS_OPENMESH
	namespace Decimater { // BEGIN_NS_DECIMATER


		//== CLASS DEFINITION =========================================================

		/** Decimating module to avoid flipping of faces.
		*  
		*  This module can be used only as a binary module. The criterion
		*  of allowing/disallowing the collapse is the angular deviation between
		*  the face normal of the orignal faces and normals of the faces after the
		*  collapse. The collapse will pass the test, if the deviation is below
		*  a given threshold.
		*/	      
		template <typename DecimaterT>
		class ModNormalFlippingT : public ModBaseT< DecimaterT >
		{
		public:

			DECIMATING_MODULE( ModNormalFlippingT, DecimaterT, NormalFlipping );

			typedef typename Mesh::Scalar  Scalar;


			ModNormalFlippingT( DecimaterT &_dec) 
				: Base(_dec, true), mesh_(Self::mesh())
			{
				set_max_normal_deviation( 90.0f );
			}



			/** Compute collapse priority due to angular deviation of face normals
			*  before and after a collapse.
			*
			*  -# Compute for each adjacent face of \c _ci.v0 the face
			*  normal if the collpase would be executed.  
			*
			*  -# Prevent the collapse, if the angle between the original and the
			*  new normal is below a given threshold.
			*  
			*  \param _ci The collapse description
			*  \return LEGAL_COLLAPSE or ILLEGAL_COLLAPSE
			*
			*  \see set_max_normal_deviation()
			*/
			float collapse_priority(const CollapseInfo& _ci)
			{
				// simulate collapse
				mesh_.set_point(_ci.v0, _ci.p1);

				// check for flipping normals
				typename Mesh::ConstVertexFaceIter vf_it(mesh_, _ci.v0);
				typename Mesh::FaceHandle          fh;
				typename Mesh::Scalar              c(1.0);

				for (; vf_it; ++vf_it) 
				{
					fh = vf_it.handle();
					if (fh != _ci.fl && fh != _ci.fr)
					{
						typename Mesh::Normal n1 = mesh_.normal(fh);
						typename Mesh::Normal n2 = mesh_.calc_face_normal(fh);

						c = dot(n1, n2);

						if (c < min_cos_)
							break;
					}
				}

				// undo simulation changes
				mesh_.set_point(_ci.v0, _ci.p0);

				return float( (c < min_cos_) ? 
					Self::ILLEGAL_COLLAPSE : 
				Self::LEGAL_COLLAPSE );
			}



			/// get normal deviation
			Scalar max_normal_deviation() const { return max_deviation_ / M_PI * 180.0; }


			/** Set normal deviation
			*  
			*  Set the maximum angular deviation of the orignal normal and the new
			*  normal in degrees.
			*/
			void set_max_normal_deviation(Scalar _f) { 
				max_deviation_ = (Scalar)(_f / 180.0 * M_PI); 
				min_cos_       = cos(max_deviation_);
			}



			// only binary mode possible
			void set_binary(bool _b) 
			{
				if (!_b)
				{
					std::cerr << "ModNormalFlippingT can only be used binary\n";
					exit(1);
				}
			}


		private:

			Mesh&   mesh_;
			Scalar  max_deviation_, min_cos_;
		};


		//=============================================================================
	} // END_NS_DECIMATER
} // END_NS_OPENMESH
//=============================================================================
#endif // OPENACG_MODNORMALFLIPPING_HH defined
//=============================================================================

